package com.linln.admin.app.service.branch;

import com.linln.admin.app.constants.AppConstants;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.StringUtils;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.ListBranchCommand;
import org.eclipse.jgit.api.MergeResult;
import org.eclipse.jgit.api.Status;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.transport.RefSpec;
import org.eclipse.jgit.transport.UsernamePasswordCredentialsProvider;

import java.io.File;
import java.io.IOException;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

@Slf4j
public class JGitBranchHandler extends BranchHelper.BranchHandler {
    static final String baseBranch = "master";

    @Override
    public boolean support(String type) {
        return AppConstants.REPO_TYPE_GIT.equals(StringUtils.trim(type));
    }

    public Map<String, BranchHelper.CommitLog> branchList(String tmpWorkDir, String url, String account, String pass) throws Exception {
        File tmpProjectDir = fetchCode(tmpWorkDir, url, account, pass, baseBranch);
        Map<String, BranchHelper.CommitLog> ret = new LinkedHashMap<>();
        if (!tmpProjectDir.isDirectory()) {
            return ret;
        }
        Git git = Git.open(tmpProjectDir);
        try {
            List<Ref> list = git.branchList().setListMode(ListBranchCommand.ListMode.REMOTE).call();
            for (Ref r : list) {
                String ver = r.getName();
                if (ver.contains("remotes/origin/")) {
                    ver = ver.substring(ver.indexOf("remotes/origin/") + "remotes/origin/".length());
                }
                if (ver.startsWith("* ")) {
                    ver = ver.substring("* ".length());
                }
                if (!ver.equals(baseBranch)) {
                    git.checkout().setCreateBranch(true).setName(ver).call();
                }
                ObjectId objId = git.getRepository().resolve("origin/" + ver);
                Iterable<RevCommit> allCommitsLater = git.log().add(objId).call();
                RevCommit lastLog = allCommitsLater.iterator().next();
                //这里只取最新的提交记录就行
                String username = lastLog.getAuthorIdent().getName();
                String email = lastLog.getAuthorIdent().getEmailAddress();
                Date time = lastLog.getAuthorIdent().getWhen();
                BranchHelper.CommitLog log = new BranchHelper.CommitLog();
                log.setCommitId(lastLog.getId().getName());
                log.setCommitTime(time);
                log.setUser(username);
                log.setEmail(email);
                ret.put(ver, log);
            }
        } finally {
            git.close();
        }

        return ret;
    }


    /**
     * 创建一个新分支，指定从哪个分支为基准
     *
     * @param url  主干url或来源url
     * @param orig 源分支
     * @param dest 目标分支
     * @return 成功-true；其它-false
     */
    @Override
    public boolean createNewBranch(String tmpWorkDir, String url, String account, String pass, String orig, String dest) throws Exception {
        File tmpProjectDir = fetchCode(tmpWorkDir, url, account, pass, orig);
        if (!tmpProjectDir.isDirectory()) {
            return false;
        }
        Git git = Git.open(tmpProjectDir);
        try {
            Ref ref = git.branchCreate().setName(dest).call();
            //推送到远程
            git.push().setCredentialsProvider(new UsernamePasswordCredentialsProvider(account, pass)).add(ref).call();

            Status status = git.status().call();
            if (status == null) {
                return false;
            }
        } finally {
            git.close();
            try {
                FileUtils.deleteDirectory(tmpProjectDir);
            } catch (Exception e) {
            }
        }
        return true;
    }

    /**
     * 合并两个分支代码
     *
     * @param url  主干url或来源url
     * @param orig 源分支
     * @param dest 目标分支
     * @return 成功-true；有冲突-false
     */
    @Override
    public boolean mergeBranch(String tmpWorkDir, String url, String account, String pass, String orig, String dest) throws Exception {
        String projectDir = FilenameUtils.getBaseName(url);
        File tmpProjectDir = fetchCode(tmpWorkDir, url, account, pass, dest);
        if (!tmpProjectDir.isDirectory()) {
            throw new IllegalArgumentException(projectDir + "项目的分支[" + dest + "]不存在!");//如果目录不存在，则表示分支不存在，就会合并失败
        }
        Git git = Git.open(tmpProjectDir);
        try {
            ObjectId objectId = git.getRepository().resolve("origin/" + orig);
            MergeResult res = git.merge().include(objectId).call();

            if (res.getMergeStatus().equals(MergeResult.MergeStatus.CONFLICTING)) {
                return false;
            }
            git.push().setCredentialsProvider(new UsernamePasswordCredentialsProvider(account, pass)).setRemote("origin").add(dest).call();
            return true;
        } finally {
            git.close();
            try {
                FileUtils.deleteDirectory(tmpProjectDir);
            } catch (Exception e) {
            }
        }
    }

    @Override
    public File checkoutBranch(String tmpWorkDir, String url, String account, String pass, String branch) throws Exception {
        File tmpProjectDir = fetchCode(tmpWorkDir, url, account, pass, branch);
        if (!tmpProjectDir.isDirectory()) {
            return null;
        }
        return tmpProjectDir;
    }

    @Override
    public boolean deleteBranch(String tmpWorkDir, String url, String account, String pass, String branch) throws Exception {
        String projectDir = FilenameUtils.getBaseName(url);
        File tmpProjectDir = fetchCode(tmpWorkDir, url, account, pass, "master");
        if (!tmpProjectDir.isDirectory()) {
            throw new IllegalArgumentException(projectDir + "项目的分支[" + branch + "]不存在!");//如果目录不存在，则表示分支不存在，就会合并失败
        }
        Git git = Git.open(tmpProjectDir);
        try {
            git.branchDelete().setBranchNames(branch).setForce(true).call();
            RefSpec refSpec = new RefSpec()
                    .setSource(null)
                    .setDestination("refs/heads/" + branch);
            git.push().setCredentialsProvider(new UsernamePasswordCredentialsProvider(account, pass)).setRefSpecs(refSpec).setRemote("origin").call();
        } finally {
            git.close();
            try {
                FileUtils.deleteDirectory(tmpProjectDir);
            } catch (Exception e) {
            }
        }
        return true;
    }

    /**
     * 摘取指定分支的代码
     *
     * @param tmpWorkDir
     * @param url
     * @param branch
     * @return
     * @throws IOException
     * @throws InterruptedException
     */
    private File fetchCode(String tmpWorkDir, String url, String account, String pass, String branch) throws Exception {
        log.info("开始拉取代码：" + url);
        String projectDir = FilenameUtils.getName(url);
        if (StringUtils.isBlank(projectDir)) {
            log.warn("git的url不正确：" + url);
            return new File("NOT_EXISTS_DIR/GIT_CHECKOUT");
        }
        if (projectDir.toLowerCase().endsWith(".git")) {
            projectDir = projectDir.substring(0, projectDir.length() - 4);
        }
        String workDir = AppConstants.GIT_WORK_DIR + "/" + tmpWorkDir;
        if (!new File(workDir).isDirectory()) {
            new File(workDir).mkdirs();
        }
        File projectCodeDir = new File(workDir, projectDir);
        if (projectCodeDir.isDirectory()) {
            FileUtils.deleteDirectory(projectCodeDir);
            try {
                projectCodeDir.delete();
            } catch (Exception e) {
            }
        }
        //把url拼接上账号和密码
        String remoteUrl = "";
        remoteUrl += url.substring(0, url.indexOf("://")) + "://";
        url = url.substring(url.indexOf("://") + 3);
        String domain = url.substring(0, url.indexOf("/"));
        if (domain.contains("@")) {
            domain = domain.substring(domain.indexOf("@") + 1);
        }
        remoteUrl += domain + url.substring(url.indexOf("/"));
        if (!remoteUrl.endsWith(".git")) {
            remoteUrl += ".git";
        }
        Status status;
        Git git = null;
        try {
            git = Git.cloneRepository()
                    .setURI(remoteUrl)
                    .setCredentialsProvider(new UsernamePasswordCredentialsProvider(account, pass))
                    .setBranch(branch)
                    .setDirectory(projectCodeDir).call();
            status = git.status().call();
        } finally {
            try {
                git.close();
            } catch (Exception e) {
            }
        }
        if (status == null) {
            log.warn("出错的命令：" + "git clone -b " + branch + " " + remoteUrl);
            return new File("NOT_EXISTS_DIR/GIT_CHECKOUT");
        }
        return projectCodeDir;
    }
}
